package com.gnerv.battle.boot.framework.advice;

import com.gnerv.battle.boot.framework.common.annotation.api.BattleApiRequestDecrypt;
import com.gnerv.battle.boot.framework.common.exception.BattleBusinessException;
import com.gnerv.battle.boot.framework.common.tools.AesTools;
import com.gnerv.battle.boot.framework.service.impl.ManageApiCryptoAesService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

/**
 * 请求解密类
 *
 * @author hermes-di
 * @since 1.0.0.RELEASE
 */
@Slf4j
@RestControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {

    @Resource
    private ManageApiCryptoAesService manageApiCryptoAesService;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        if (inputMessage.getBody().available() <= 0) {
            return inputMessage;
        }
        boolean hasMethodAnnotation = methodParameter.hasMethodAnnotation(BattleApiRequestDecrypt.class);
        String decryptUkId = inputMessage.getHeaders().getFirst("Battle-Api-Decrypt");
        String decrypt = "";
        if (hasMethodAnnotation) {
            if (StringUtils.hasText(decryptUkId)) {
                try {
                    decrypt = decrypt(inputMessage, decryptUkId);
                } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException |
                         NoSuchAlgorithmException | BadPaddingException | InvalidKeyException |
                         InvalidKeySpecException e) {
                    throw new RuntimeException(e);
                }
            } else {
                throw new BattleBusinessException("header [Battle-Api-Decrypt] was empty");
            }
        } else if (StringUtils.hasText(decryptUkId)) {
            try {
                decrypt = decrypt(inputMessage, decryptUkId);
            } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException |
                     NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | InvalidKeySpecException e) {
                throw new RuntimeException(e);
            }
        } else {
            return inputMessage;
        }
        byte[] requestDataByte = decrypt.getBytes(StandardCharsets.UTF_8);
        // 使用解密后的数据，构造新的读取流
        InputStream rawInputStream = new ByteArrayInputStream(requestDataByte);
        return new HttpInputMessage() {
            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }

            @Override
            public InputStream getBody() {
                return rawInputStream;
            }
        };
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    private String decrypt(HttpInputMessage inputMessage, String decryptUkId) throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
        byte[] requestDataByte = new byte[inputMessage.getBody().available()];
        inputMessage.getBody().read(requestDataByte);
        try {
            requestDataByte = new byte[inputMessage.getBody().available()];
            inputMessage.getBody().read(requestDataByte);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String requestData = new String(requestDataByte, StandardCharsets.UTF_8);
        // 解密
        return decryptAes(requestData, decryptUkId);
    }

    private String decryptAes(String requestData, String decryptUkId) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        String key =  manageApiCryptoAesService.selectKeyByUkId(decryptUkId);
        if (key == null) {
            throw new BattleBusinessException("Secret Key is null");
        }
        return AesTools.decrypt(requestData, key.getBytes(StandardCharsets.UTF_8));
    }

}